package com.hdu.dwh.service.impl.cube_impl;

import java.util.*;
import java.util.stream.Collectors;

import com.hdu.dwh.pojos.*;
import com.hdu.dwh.utils.OperatorUtil;
import com.hdu.dwh.utils.StatementUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hdu.dwh.dao.PaimonBaseDao;
import com.hdu.dwh.dtos.cube_dtos.CubeDimensionDto;
import com.hdu.dwh.dtos.cube_dtos.CuboidDimensionDto;
import com.hdu.dwh.dtos.cube_dtos.CuboidMetricDto;
import com.hdu.dwh.dtos.cube_dtos.FilterDto;
import com.hdu.dwh.enums.Hierachy;
import com.hdu.dwh.mapper.cube_mapper.CubeMapper;
import com.hdu.dwh.pojos.cube_pojos.Cube;
import com.hdu.dwh.pojos.cube_pojos.CubeDimension;
import com.hdu.dwh.pojos.cube_pojos.Cuboid;
import com.hdu.dwh.pojos.cube_pojos.CuboidDimension;
import com.hdu.dwh.pojos.cube_pojos.Filter;
import com.hdu.dwh.pojos.source_access_pojos.DataWarehouseDatabase;
import com.hdu.dwh.pojos.source_access_pojos.DataWarehouseTable;
import com.hdu.dwh.pojos.cube_pojos.CubeMetric;
import com.hdu.dwh.service.DataWarehouseDatabaseService;
import com.hdu.dwh.service.DataWarehouseTableService;
import com.hdu.dwh.service.ModelService;
import com.hdu.dwh.service.TableRelationService;
import com.hdu.dwh.service.TrinoQueryService;
import com.hdu.dwh.service.cube_service.CubeDimensionService;
import com.hdu.dwh.service.cube_service.CubeMetricService;
import com.hdu.dwh.service.cube_service.CubeService;
import com.hdu.dwh.service.cube_service.CuboidDimensionService;
import com.hdu.dwh.service.cube_service.CuboidService;
import com.hdu.dwh.service.cube_service.FilterService;
import com.hdu.dwh.utils.CubeUtils;
import com.hdu.dwh.vos.CubeVo;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class CubeServiceImpl extends ServiceImpl<CubeMapper, Cube> implements CubeService {

    private final PaimonBaseDao paimonBaseDao;

    @Autowired
    private CuboidService cuboidService;

    @Autowired
    private CubeDimensionService cubeDimensionService;

    @Autowired
    private CubeMetricService cubeMerticService;

    @Autowired
    private CuboidDimensionService cuboidDimensionService;

    @Autowired
    private DataWarehouseDatabaseService dwhDatabaseService;

    @Autowired
    private DataWarehouseTableService dwhTableService;

    @Autowired
    private ModelService modelService;

    @Autowired
    private FilterService filterService;

    @Autowired
    private TableRelationService tableRelationService;

    @Autowired
    private TrinoQueryService trinoQueryService;

    CubeServiceImpl(PaimonBaseDao paimonBaseDao) {
        this.paimonBaseDao = paimonBaseDao;
    }

    @Override
    public Set<Cuboid> buildCuboids(Cube cube, Set<CubeDimension> dimensions) {
        
        // 构建维度的幂集
        Set<Set<CuboidDimensionDto>> dimensionPowerSet = CubeUtils.getDimensionPowerSet(dimensions);
        Set<Cuboid> cuboids = new LinkedHashSet<>();
        // 根据维度幂集来构建所有的cuboid
        for (Set<CuboidDimensionDto> dimSet : dimensionPowerSet) {
            Integer cubeId = cube.getId();
            String tag = dimSet.stream().map(d -> d.getField()).collect(Collectors.joining(","));
            Integer level = dimSet.size();
            Cuboid cuboid = new Cuboid(tag, cubeId, level);
            // 先保存获取cuboid的id
            cuboidService.save(cuboid);

            Set<CuboidDimension> cuboidDimensions = CubeUtils.CuboidConvertDimensionDtoToDimension(dimSet, cuboid);
            cuboid.setDimensions(cuboidDimensions);
            cuboids.add(cuboid);
        }

        for (Cuboid cuboid : cuboids) {
            log.info("cuboid: {}", cuboid);
            log.info("cuboid dimensions: {}", cuboid.getDimensions());
            cuboid.setPreLevelCuboids(CubeUtils.getPreLevelCuboids(cuboid.getDimensions(), cuboids));
            cuboid.setNextLevelCuboids(CubeUtils.getNextLevelCuboids(cuboid.getDimensions(), cuboids));
        }

        return cuboids;
    }

    @Override
    @Transactional
    public CubeVo createCube(Cube cube, Set<CubeDimensionDto> dimensions, Set<CuboidMetricDto> metrics, Set<FilterDto> filters) {
        cube.setCreatedBy(OperatorUtil.getCurrentOperator());
        this.save(cube);

        Set<CubeDimension> dims = CubeUtils.CubeConvertDimensionDtoToDimension(dimensions, cube);
        Set<CubeMetric> mets = CubeUtils.convertMetricDtoToMetric(metrics, cube);
        Set<Filter> flts = CubeUtils.convertFilterDtoToFilter(filters, cube);
        cube.setCuboids(this.buildCuboids(cube, dims));

        CubeVo cubeVo = new CubeVo();
        cube.setMetrics(mets);
        saveCubeProperties(cube, dims, mets, flts);
        CubeUtils.copyCubeToCubeVo(cube, cubeVo);
        return cubeVo;
    }

    private void saveCubeProperties(Cube cube, Set<CubeDimension> dims, Set<CubeMetric> mets, Set<Filter> filters) {
        this.saveCubeDimensions(dims);
        this.saveMertics(mets);
        this.saveFilters(filters);
        for (Cuboid cuboid : cube.getCuboids()) {
            if (cuboid.getDimensions() == null || cuboid.getDimensions().isEmpty()){
                continue;
            }
            this.cuboidDimensionService.saveBatch(cuboid.getDimensions());
        }
    }

    @Override
    public void saveCubeDimensions(Set<CubeDimension> dimensions) {
        this.cubeDimensionService.saveBatch(dimensions);
    }

    @Override
    public void saveMertics(Set<CubeMetric> metrics) {
        this.cubeMerticService.saveBatch(metrics);
    }

    @Override
    public void saveFilters(Set<Filter> filters) {
        this.filterService.saveBatch(filters);
    }

    @Override
    public CubeVo queryCubeDetail(Cube cube) {
        cube.setMetrics(cubeMerticService.getMetricsByCubeId(cube.getId()));
        cube.setCuboids(cuboidService.getCuboidsByCubeId(cube.getId()));

        CubeVo cubeVo = new CubeVo();
        CubeUtils.copyCubeToCubeVo(cube, cubeVo);
        
        return cubeVo;
    }

    @Override
    @Transactional
    public void deleteCube(Cube cube) {
        this.removeById(cube.getId());
        this.cubeDimensionService.removeByCubeId(cube.getId());
        this.cubeMerticService.removeByCubeId(cube.getId());
        this.filterService.removeByCubeId(cube.getId());
        this.cuboidService.removeByCubeId(cube.getId());
    }

    @Override
    public Cuboid queryCuboidDetail(Cuboid cuboid) {
        cuboid.setDimensions(this.cuboidDimensionService.getCuboidDimensionsByCuboidId(cuboid.getId()));
        return cuboid;
        
    }
    
    @Override
    public String getCuboidSQL(Cuboid cuboid, Set<DimensionFilter> filters, Set<MetricFilter> metricFilters, Set<DimensionPivot> pivots, Integer pageNum, Integer pageSize, String sqlExecutor) throws Exception {
        this.queryCuboidDetail(cuboid);
        Cube cube = this.getById(cuboid.getCubeId());
        cube.setMetrics(this.cubeMerticService.getMetricsByCubeId(cube.getId()));
        cube.setFilters(this.filterService.queryFiltersById(Hierachy.DWS, cube.getId()));
        Model model = modelService.getById(cube.getModelId());
        String databaseName = dwhDatabaseService.getById(model.getDatabaseId()).getDatabaseName();
        Integer limit = pageSize;
        Integer offset = null;
        if(pageNum != null) {
            offset = (pageNum - 1) * pageSize;
        }
        for(DimensionPivot pivot : pivots) {
            if (pivot.getDimensionValues()==null || pivot.getDimensionValues().size() == 0) {
                Set<String> distinctValues = this.trinoQueryService.queryDimensionValues(databaseName, model.getName(), pivot.getDimension());
                pivot.setDimensionValues(distinctValues);
            }
        }
        return CubeUtils.generateSql(cuboid, cube,filters, pivots, databaseName , model.getName(), limit, offset);
    }

    @Override
    public String getCuboidSQL(Cuboid cuboid, Set<DimensionFilter> filters, Integer pageNum, Integer pageSize, String sqlExecutor) throws Exception {
        return this.getCuboidSQL(cuboid, filters, Set.of(), Set.of(), pageNum, pageSize, sqlExecutor);
    }

    @Override
    @Transactional
    public void saveCuboid(Cuboid cuboid,DataWarehouseDatabase database, String saveTableName, String description) throws Exception {
        Set<DimensionFilter> dimensionFilters = Set.of();
        Set<MetricFilter> metricFilters = Set.of();
        Set<DimensionPivot> pivots = Set.of();
        String sql = this.getCuboidSQL(cuboid, dimensionFilters, metricFilters, pivots, null, null, "flink");
        Cube cube = this.getById(cuboid.getCubeId());
        // 添加tableRelation
        Model model = modelService.getById(cube.getModelId());
        DataWarehouseTable sourceModelTable = dwhTableService.getById(model.getTableId());
        
        if (dwhTableService.checkTableNameExist(database.getId(), saveTableName)) {
            throw new RuntimeException("目标表名已存在，请更换表名");
        }
        String tableName = String.format("%s", saveTableName);
        // 保存表的元信息
        DataWarehouseTable targetTable = dwhTableService.createTable(database.getId(), tableName, sourceModelTable.getId(), 3, description);
        this.tableRelationService.saveWithSourceTableIdAndTargetTableId(sourceModelTable.getId(), targetTable.getId());

        // 湖仓中创建表
        String CTASSql = String.format("CREATE TABLE `%s`.%s AS %s",  database.getDatabaseName(), tableName, sql);
        log.info("CTAS SQL: {}", CTASSql);
        this.paimonBaseDao.getFlinkBaseDao().getTableEnvironment().executeSql(CTASSql);

        // 保存Cuboid
        cuboid.setIsStored(true);
        cuboid.setTableId(targetTable.getId());
        cuboid.setCreatedBy(OperatorUtil.getCurrentOperator());
        this.cuboidService.updateById(cuboid);
    }

    @Override
    public void deleteCuboid(Cuboid cuboid) {
        if (cuboid.getIsStored()==false || cuboid.getTableId() == null || cuboid.getTableId() == null) {
            throw new RuntimeException("该cuboid未保存，无法删除");
        }
        this.dwhTableService.deleteTable(this.dwhTableService.getById(cuboid.getTableId()));
        cuboid.setIsStored(false);
        cuboid.setTableId(null);
        this.cuboidService.updateById(cuboid);
    }

    @Override
    public String getSavedCuboidSQL(Cuboid cuboid, Set<DimensionFilter> dimensionFilter,Set<DimensionPivot> pivots, Integer pageNum, Integer pageSize, String string) {
        // 如果有pivot参数，使用条件聚合实现透视表，实现旋转操作
        if (pivots != null && !pivots.isEmpty()) {
            DataWarehouseTable cuboidTable = this.dwhTableService.getById(cuboid.getTableId());
            DataWarehouseDatabase cuboidDatabase = this.dwhDatabaseService.getById(cuboidTable.getDatabaseId());
            Cube cube = this.getById(cuboid.getCubeId());
            // 确保加载cube的metrics属性
            if (cube.getMetrics() == null) {
                cube.setMetrics(this.cubeMerticService.getMetricsByCubeId(cube.getId()));
            }
            return CubeUtils.generatePivotSQL(cuboid, cube, dimensionFilter, pivots, pageNum, pageSize, this.cuboidService, cuboidTable, cuboidDatabase);
        } else {
            // 没有透视参数时使用原有逻辑
            StringBuilder cuboidBuilder = new StringBuilder();
            DataWarehouseTable cuboidTable = this.dwhTableService.getById(cuboid.getTableId());
            DataWarehouseDatabase cuboidDatabase = this.dwhDatabaseService.getById(cuboidTable.getDatabaseId());
            cuboidBuilder.append("SELECT * FROM %s.%s ".formatted(
                    StatementUtils.quoteTrino(cuboidDatabase.getDatabaseName()),
                    StatementUtils.quoteTrino(cuboidTable.getTableName())));
            // WHERE
            if (dimensionFilter != null && !dimensionFilter.isEmpty() && dimensionFilter.size() > 0) {
                cuboidBuilder.append(" WHERE ");
            }
            int index = 0;
            for (DimensionFilter filter : dimensionFilter) {
                if (index > 0) {
                    cuboidBuilder.append(" AND ");
                }
                // 对字段名进行转义，防止SQL注入
                cuboidBuilder.append(String.format("%s %s %s",
                        StatementUtils.quoteTrino(filter.getField()),
                        filter.getOperator(),
                        filter.getValue()));
                index++;
            }
            // 添加LIMIT和OFFSET子句
            if (pageNum != null && pageSize != null) {
                int offset = (pageNum - 1) * pageSize;
                cuboidBuilder.append(String.format(" OFFSET %d LIMIT %d", offset, pageSize));
            } else if (pageSize != null) {
                cuboidBuilder.append(String.format(" LIMIT %d", pageSize));
            }
            return cuboidBuilder.toString();
        }
    }

    @Override
    @Transactional
    public void deleteCuboidByTableId(Long id) {
        Cuboid cuboid = cuboidService.lambdaQuery().eq(Cuboid::getTableId, id).one();
        if (cuboid != null) {
            this.deleteCuboid(cuboid);
        } else {
            throw new IllegalArgumentException("tableId无效，未找到对应的Cuboid");
        }
    }
}
